package com.ruoyi.app.service.impl;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.app.dto.MemberInvoiceDto;
import com.ruoyi.app.mapper.ParkOrderMapper;
import com.ruoyi.app.service.IInvoiceService;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.MemberInvoice;
import com.ruoyi.domain.ParkingOrder;
import com.ruoyi.forest.WeChatClient;
import com.ruoyi.forest.WeChatInvoiceClient;
import com.ruoyi.framework.config.SmartParkingCloudConfig;
import com.ruoyi.framework.config.WxPayConfig;
import com.ruoyi.mapper.MemberInvoiceMapper;
import com.ruoyi.mapper.ParkingOrderMapper;
import com.wechat.pay.java.core.cipher.RSASigner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.util.*;

/**
 * App端Service业务层处理
 * 
 * @author dongdong
 * @date 2025-09-11
 */
@Slf4j
@Service
public class InvoiceServiceImpl implements IInvoiceService {

    @Autowired
    private ObjectMapper objectMapper;
    @Resource
    private ParkingOrderMapper parkingOrderMapper;
    @Resource
    private MemberInvoiceMapper memberInvoiceMapper;
    @Resource
    private WeChatClient weChatClient;
    @Resource
    private WeChatInvoiceClient weChatInvoiceClient;
    @Autowired
    private WxPayConfig wxPayConfig;
    @Autowired
    private RSASigner rsaSigner;
    @Value("${wechat.s_pappid}")
    private String sPappid;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public Map<String, Object> getAuthUrl(String orderNo, String uniqueId) {
        ParkingOrder parkingOrder = new ParkingOrder();
        parkingOrder.setOrderNo(orderNo);
        parkingOrder = parkingOrderMapper.selectParkingOrder(parkingOrder);
        Optional.ofNullable(parkingOrder).orElseThrow(() -> new RuntimeException("无效的订单"));
        if (!parkingOrder.getUniqueId().equals(uniqueId)){
            throw new RuntimeException("无效的订单");
        }
        String token = getAccessToken();
        // 获取ticket
        Map<String, Object> ticketMap = weChatClient.getTicket(token, "wx_card");
        String ticket = ticketMap.get("ticket").toString();

        // 准备请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("s_pappid", sPappid); // 开票平台在微信的标识号，商户需要找开票平台提供
        params.put("order_id", orderNo);
        params.put("money", parkingOrder.getPayAmount().multiply(new BigDecimal(100))); // 单位（分）
        params.put("timestamp", System.currentTimeMillis() / 1000);
        params.put("source", "wxa"); // 开票来源，app：app开票，web：微信h5开票，wxa：小程序开发票，wap：普通网页开票
        // params.put("redirect_url", "YOUR_REDIRECT_URL"); // 如果需要的话
        params.put("ticket", ticket);
        params.put("type", 1); // 授权类型，0：开票授权，1：填写字段开票授权，2：领票授权
        return weChatInvoiceClient.getAuthUrl(token, params);
    }

    @Override
    public Map<String, Object> getAuthData(String orderNo) {
        String token = getAccessToken();
        Map<String, Object> params = new HashMap<>();
        params.put("order_id", orderNo);
        params.put("s_pappid", sPappid);
        return weChatInvoiceClient.getAuthData(token, params);
    }


    @Override
    public Map<String, Object> createTemplate() {
        /*Map<String, Object> baseInfo = new HashMap<>();
        baseInfo.put("logo_url", "https://zhtc.lssmartcity.com/profile/logo.png");
        baseInfo.put("title", "拉萨高新智慧城市运营管理有限公司");
        baseInfo.put("custom_url_name", "xyz");
        baseInfo.put("custom_url", "xyz");
        baseInfo.put("custom_url_sub_title", "xyz");
        baseInfo.put("promotion_url_name", "puname");
        baseInfo.put("promotion_url", "purl");
        baseInfo.put("promotion_url_sub_title", "ptitle");

        Map<String, Object> invoiceInfo = new HashMap<>();
        invoiceInfo.put("base_info", baseInfo);
        invoiceInfo.put("type", "西藏自治区增值税普通发票");
        invoiceInfo.put("payee", "测试-收款方");

        Map<String, Object> result = new HashMap<>();
        result.put("invoice_info", invoiceInfo);*/

        // 获取ACCESS_TOKEN
        Map<String, Object> tokenMap = weChatClient.getWxToken(SmartParkingCloudConfig.APPID, SmartParkingCloudConfig.SECRET);
        String token = tokenMap.get("access_token").toString();
        // 获取s_pappid
        /*Map<String, Object> invoiceUrlMap = weChatInvoiceClient.setUrlInvoice(token, new HashMap<>());
        System.out.println(invoiceUrlMap.get("invoice_url"));*/

       /* String phone = "15289080131";
        String contactBody = "{\"contact\": {\"time_out\": " + 3600 + ", \"phone\": \"" + phone + "\"}}";
        JSONObject contactJson =  JSON.parseObject(contactBody);
        Map<String, Object> contactMap = weChatInvoiceClient.setBizattr(token, contactJson);
        System.out.println(contactMap);*/

        // 获取ticket
        Map<String, Object> ticketMap = weChatClient.getTicket(token, "wx_card");
        String ticket = ticketMap.get("ticket").toString();
        System.out.println(ticket);

        // 准备请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("s_pappid", sPappid); // 开票平台在微信的标识号，商户需要找开票平台提供
        params.put("order_id", "ORD20250831135021093");
        params.put("money", "18.75"); // 单位（分）
        params.put("timestamp", System.currentTimeMillis() / 1000);
        params.put("source", "wxa"); // 开票来源，app：app开票，web：微信h5开票，wxa：小程序开发票，wap：普通网页开票
        // params.put("redirect_url", "YOUR_REDIRECT_URL"); // 如果需要的话
        params.put("ticket", ticket);
        params.put("type", 1); // 授权类型，0：开票授权，1：填写字段开票授权，2：领票授权
        return weChatInvoiceClient.getAuthUrl(token, params);
    }

    @Override
    public Map<String, Object> baseInformation() {
        // 构建请求头
        String authorization = buildAuthorizationHeader("GET",  "/v3/new-tax-control-fapiao/merchant/base-information", null);
        Map<String, Object> headers = new HashMap<>();
        headers.put("Accept", "application/json");
        headers.put("Authorization", authorization);
        return weChatInvoiceClient.baseInformationInvoice(headers);
    }

    @Override
    public Boolean apply(MemberInvoiceDto memberInvoiceDto) {
        MemberInvoice memberInvoice = ParkOrderMapper.INSTANCE.toMemberInvoice(memberInvoiceDto);
        String applyId = StringUtils.generateInvoiceApplyId();
        memberInvoice.setApplyId(applyId);
        ParkingOrder parkingOrder = new ParkingOrder();
        parkingOrder.setOrderNo(memberInvoiceDto.getOrderNo());
        parkingOrder = parkingOrderMapper.selectParkingOrder(parkingOrder);
        memberInvoice.setAmount(parkingOrder.getPayAmount());
        int res = memberInvoiceMapper.insertMemberInvoice(memberInvoice);
        applyForInvoice(memberInvoice, parkingOrder);
        // 更新本地发票状态
        //memberInvoiceMapper.updateStatus(fapiaoId, 1);
        return null;
    }

    public void applyForInvoice(MemberInvoice memberInvoice, ParkingOrder parkingOrder) {
        // 构建请求参数
        Map<String, Object> params = new HashMap<>();
        params.put("mchid", wxPayConfig.getMchId());
        params.put("scene",  "WITH_WECHATPAY");
        params.put("fapiao_apply_id", memberInvoice.getApplyId());

        // 设置购买方信息
        Map<String, Object> buyer_information = new HashMap<>();
        int amount = memberInvoice.getAmount().multiply(new BigDecimal(100)).intValue();
        buyer_information.put("type", memberInvoice.getInvoiceType().intValue() == 1 ? "INDIVIDUAL" : "ORGANIZATION");
        buyer_information.put("name", memberInvoice.getTitle());
        buyer_information.put("taxpayer_id", memberInvoice.getTaxNumber());
        buyer_information.put("address", memberInvoice.getCompanyAddress());
        buyer_information.put("telephone", memberInvoice.getTelephone());
        buyer_information.put("bank_name", memberInvoice.getBankName());
        buyer_information.put("bank_account", memberInvoice.getBankAccount());
        buyer_information.put("amount", amount);
        buyer_information.put("out_trade_no", memberInvoice.getOrderNo());
        params.put("buyer_information", buyer_information);

        // 设置发票信息
        Map<String, Object> fapiao_information = new HashMap<>();
        fapiao_information.put("fapiao_id", memberInvoice.getApplyId());
        fapiao_information.put("total_amount", amount);
        fapiao_information.put("remark", parkingOrder.getParkName()+" - 停车费发票");
        List<Map<String, Object>> items = new ArrayList<>();

        // 发票行项目
        Map<String, Object> item = new HashMap<>();
        item.put("tax_code", "3010101020203000000");
        item.put("goods_category", "3010101020203000000");
        item.put("goods_name", "3010101020203000000");
        item.put("goods_id", "3010101020203000000");
        item.put("specification", "3010101020203000000");
        item.put("unit", "3010101020203000000");
        item.put("quantity", 100000000);
        item.put("total_amount", amount);
        item.put("tax_rate", "3%");
        item.put("tax_prefer_mark", "NO_FAVORABLE");
        item.put("discount", false);
        items.add(item);
        fapiao_information.put("items", items);

        List<Map<String, Object>> fapiaoInformations = new ArrayList<>();
        fapiaoInformations.add(fapiao_information);
        params.put("fapiao_information", fapiaoInformations);

        try {
            log.info("开始调用微信开具电子发票接口，发票申请单号: {}", memberInvoice.getApplyId());

            String bodyJson = objectMapper.writeValueAsString(params);
            String authorization = buildAuthorizationHeader("POST",  "/v3/new-tax-control-fapiao/fapiao-applications", bodyJson);
            // 构建请求头
            Map<String, Object> headers = new HashMap<>();
            headers.put("Accept", "application/json");
            headers.put("Content-Type", "application/json");
            headers.put("Authorization", authorization);
            headers.put("Wechatpay-Serial", wxPayConfig.getMchSerialNo());
            System.out.println(headers);
            Map<String,Object> response = weChatInvoiceClient.applyForInvoice(headers, params);

            log.info("微信开具电子发票接口调用成功: {}", response);

        } catch (Exception e) {
            log.error("调用微信开具电子发票接口失败，发票申请单号: {}, 错误信息: {}", memberInvoice.getApplyId(), e.getMessage(), e);
            throw new RuntimeException("电子发票开具失败: " + e.getMessage());
        }
    }

    /**
     * 使用现有的RSASigner构建微信支付V3认证请求头（推荐方式）
     * @param method HTTP方法
     * @param url API接口路径
     * @param requestBody 请求体JSON字符串
     * @return 完整的Authorization头值
     */
    public String buildAuthorizationHeader(String method, String url, String requestBody) {
        try {
            // 生成随机字符串和时间戳
            String nonceStr = StringUtils.generateNonceStr();
            String timestamp = String.valueOf(Instant.now().getEpochSecond());

            // 构建签名串
            String message = method + "\n" +
                    url + "\n" +
                    timestamp + "\n" +
                    nonceStr + "\n" +
                    (requestBody != null ? requestBody : "") + "\n";

            // 使用配置的RSASigner进行签名
            String signature = rsaSigner.sign(message).getSign();

            // 构造Authorization头
            return String.format(
                    "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\",timestamp=\"%s\",serial_no=\"%s\"",
                    wxPayConfig.getMchId(), nonceStr, signature, timestamp, wxPayConfig.getMchSerialNo()
            );
        } catch (Exception e) {
            log.error("构建Authorization头失败: {}", e.getMessage());
            throw new RuntimeException("构建微信认证头失败", e);
        }
    }

    public void queryInvoice(String fapiaoApplyId) {
        // 调用微信查询电子发票API
        //return wechatPayApi.queryInvoice(fapiaoApplyId);
    }

    /** 获取可用 token（先 Redis，没有再刷新） */
    public String getAccessToken() {
        String tokenKey = "wechat_access_token";
        String token = redisTemplate.opsForValue().get(tokenKey);
        if (StrUtil.isNotBlank(token)) {
            return token;
        }
        // 分布式锁，防止多节点并发刷新
        String lockKey = tokenKey + ":lock";
        Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", Duration.ofSeconds(10));
        if (Boolean.TRUE.equals(locked)) {
            try {
                // 二次校验，防止排队线程重复刷新
                token = redisTemplate.opsForValue().get(tokenKey);
                if (StrUtil.isBlank(token)) {
                    token = refreshFromWx();
                }
                return token;
            } finally {
                redisTemplate.delete(lockKey);
            }
        }
        // 没抢到锁的线程稍等再读
        ThreadUtil.sleep(200);
        return getAccessToken();
    }

    /** 调微信接口刷新并写 Redis，7100 秒过期 */
    private String refreshFromWx() {
        // 获取ACCESS_TOKEN
        Map<String, Object> tokenMap = weChatClient.getWxToken(SmartParkingCloudConfig.APPID, SmartParkingCloudConfig.SECRET);
        String token = tokenMap.get("access_token").toString();
        if (StrUtil.isBlank(token)) {
            log.error("获取微信 access_token 失败：{}", tokenMap);
            throw new RuntimeException("微信 token 获取失败");
        }
        redisTemplate.opsForValue().set("wechat_access_token", token, Duration.ofSeconds(7100));
        return token;
    }
}
